package com.lagou.edu.factory;

import com.lagou.edu.annotation.Autowired;
import com.lagou.edu.annotation.Service;
import com.lagou.edu.annotation.Transactional;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.beans.factory.BeanDefinitionStoreException;
import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.context.annotation.ScannedGenericBeanDefinition;
import org.springframework.core.env.StandardEnvironment;
import org.springframework.core.io.Resource;
import org.springframework.core.io.support.PathMatchingResourcePatternResolver;
import org.springframework.core.io.support.ResourcePatternResolver;
import org.springframework.core.type.classreading.CachingMetadataReaderFactory;
import org.springframework.core.type.classreading.MetadataReader;
import org.springframework.util.ClassUtils;
import org.springframework.util.StringUtils;

import javax.servlet.ServletContext;
import javax.servlet.http.HttpServletRequest;
import java.io.IOException;
import java.lang.reflect.Field;
import java.util.HashMap;
import java.util.LinkedHashSet;
import java.util.Map;
import java.util.Set;

//import org.springframework.context.config.SpringConfiguredBeanDefinitionParser;

/**
 * 注解工厂类，
 */
public class AnnotationBeanFactory{

    public static void main(String[] args) {

    }

    public static void sout() {
    }

    /**
     * 保存实体类对象
     */
    private static Map<String, Object> objectHashMap = new HashMap<>();

    static {
        Set<BeanDefinition> beanDefinitions = scanCandidateComponents("com.lagou.edu");//获取该基础路径下的每一个对象实例
        for (BeanDefinition beanDefinition : beanDefinitions) { //第一个循环，将所有标注了@Service的类的实例生成之后放入objectHashMap中
            String beanCLassName = beanDefinition.getBeanClassName();
            Class<?> aClass = null;
            if (beanCLassName.endsWith("$1")) {
                continue;
            }
            try {
                aClass = Class.forName(beanCLassName);
                if (aClass.getAnnotation(Service.class) != null) {//类上添加了@Service注解
                    String value = aClass.getAnnotation(Service.class).value();
                    String className = null;
                    if (!StringUtils.isEmpty(value)) {
                        className = value;
                    } else {
                        String[] split = beanCLassName.split("[.]");
                        className = split[split.length - 1];
                        className = (new StringBuilder()).append(Character.toLowerCase(className.charAt(0))).append(className.substring(1)).toString();
                    }
                    System.out.println(beanCLassName + "-----" + className);
                    Object classObject = aClass.newInstance();
                    objectHashMap.put(className, classObject);
                }
            } catch (ClassNotFoundException e) {
                e.printStackTrace();
                continue;
            } catch (NoClassDefFoundError e) {
                continue;
            } catch (IllegalAccessException e) {
                e.printStackTrace();
            } catch (InstantiationException e) {
                e.printStackTrace();
            }
        }
        for (BeanDefinition beanDefinition : beanDefinitions) {//第二个循环，注入所有有@Autowired的对象实例
            String beanCLassName = beanDefinition.getBeanClassName();
            Class<?> aClass = null;
            if (beanCLassName.endsWith("$1")) {
                continue;
            }
            try {
                aClass = Class.forName(beanCLassName);
                Object clazz = aClass.newInstance();
                clazz = insertObject(clazz);
                if (aClass.getAnnotation(Service.class) != null) {//类上添加了@Service注解
                    String value = aClass.getAnnotation(Service.class).value();
                    String className = null;
                    if (!StringUtils.isEmpty(value)) {
                        className = value;
                    } else {
                        String[] split = beanCLassName.split("[.]");
                        className = split[split.length - 1];
                        className = (new StringBuilder()).append(Character.toLowerCase(className.charAt(0))).append(className.substring(1)).toString();
                    }
                    System.out.println(beanCLassName + "-----" + className);
                    objectHashMap.put(className, clazz);
                }
            } catch (ClassNotFoundException e) {
                e.printStackTrace();
                continue;
            } catch (NoClassDefFoundError e) {
                e.printStackTrace();
                continue;
            } catch (IllegalAccessException e) {
                e.printStackTrace();
            } catch (InstantiationException e) {//newInstance出错，不存在无参init方法
                continue;
            }
        }
        for (String className : objectHashMap.keySet()) {//第三个循环(循环所有已添加@server注解的对象)，生成所有有@Transactional注解的对象的代理对象
            Object clazz = objectHashMap.get(className);
            Transactional annotation = clazz.getClass().getAnnotation(Transactional.class);
            if (annotation != null) {
                ProxyFactory proxyFactory = (ProxyFactory) objectHashMap.get("proxyFactory");
                Object jdkProxy = proxyFactory.getJdkProxy(clazz);
//                transObjectHashMap.put(className, jdkProxy);
                objectHashMap.put(className,jdkProxy);
            }
        }
    }

    /**
     * 注入对象（多层实例也可以）
     *
     * @param aClass
     * @return
     * @throws IllegalAccessException
     * @throws InstantiationException
     */
    private static Object insertObject(Object aClass) throws IllegalAccessException, InstantiationException {
        Field[] fields = aClass.getClass().getDeclaredFields();
        for (Field field : fields) {
            if (field.getAnnotation(Autowired.class) != null) {//类中变量上添加了@Autowired注解
                String fieldName = field.getGenericType().getTypeName();
                String[] split = fieldName.split("[.]");
                fieldName = split[split.length - 1];
                fieldName = (new StringBuilder()).append(Character.toLowerCase(fieldName.charAt(0))).append(fieldName.substring(1)).toString();
                System.out.println(aClass.getClass().getName() + ",fieldName:" + fieldName);
                Object fieldOb = objectHashMap.get(fieldName);
                Field[] fields2 = fieldOb.getClass().getDeclaredFields();
                for (Field field1 : fields2) {
                    if (field1.getAnnotation(Autowired.class) != null) {
                        fieldOb = insertObject(fieldOb);
                    }
                }
                field.setAccessible(true);
                field.set(aClass, fieldOb);//变量赋值,要注意多级子类的情况发生
            }
        }
        return aClass;
    }

    protected final static Log logger = LogFactory.getLog(AnnotationBeanFactory.class);


    private static Set<BeanDefinition> scanCandidateComponents(String basePackage) {
        Set<BeanDefinition> candidates = new LinkedHashSet<>();
        try {
            String packageSearchPath = ResourcePatternResolver.CLASSPATH_ALL_URL_PREFIX +
                    ClassUtils.convertClassNameToResourcePath(new StandardEnvironment().resolveRequiredPlaceholders(basePackage)) + '/' + "**/*.class";
            Resource[] resources = new PathMatchingResourcePatternResolver().getResources(packageSearchPath);
            for (Resource resource : resources) {
                if (resource.isReadable()) {
                    try {
                        MetadataReader metadataReader = new CachingMetadataReaderFactory().getMetadataReader(resource);
                        ScannedGenericBeanDefinition sbd = new ScannedGenericBeanDefinition(metadataReader);
                        sbd.setResource(resource);
                        sbd.setSource(resource);
                        candidates.add(sbd);
                    } catch (Throwable ex) {
                        throw new BeanDefinitionStoreException(
                                "Failed to read candidate component class: " + resource, ex);
                    }
                } else {
                }
            }
        } catch (IOException ex) {
            throw new BeanDefinitionStoreException("I/O failure during classpath scanning", ex);
        }
        return candidates;
    }

    //提供一个获取对象实例的方法
    public static Object getBean(String id){
        return objectHashMap.get(id);
    }
}
